/*!
** @mainpage
** This is the documentation for the Firefox add-on Keep My Bookmarks !
** You will be able to see prototypes of each function, and  how they are
** interacting which each others.<br />
** Website : http://adrien.frenchcomp.net
*/

/*!
** @file keepmybookmarks.js
** @brief main file
** @author Adrien MOGENET
*/


////////////////////////////////////////////////////////////////////////////////
//
// CONSTANTS
//
////////////////////////////////////////////////////////////////////////////////


/*!
** @def DEBUG	: state of the debug mode
** @todo set @c DEBUG to false before packaging !
*/
const DEBUG		= false;
const VERSION		= 0.1;
const APP_URI		= "chrome://keepmybookmarks";
const MODE_NETWORK	= 0;
const MODE_LOCAL	= 1;


////////////////////////////////////////////////////////////////////////////////
//
// GLOBAL OBJECTS
//
////////////////////////////////////////////////////////////////////////////////


const nsIFilePicker	= Components.interfaces.nsIFilePicker;
var historyService	= Components.classes["@mozilla.org/browser/nav-history-service;1"]
			.getService (Components.interfaces.nsINavHistoryService);
var bookmarksService	= Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
			.getService (Components.interfaces.nsINavBookmarksService);

/*!
** @var root : long long
** @brief (id of the root folder for bookmarks)
** @todo User will be able to select the root directory
*/
var root		= bookmarksService.bookmarksMenuFolder;

/*!
** @var listOfBookmarks : string.
** @brief Store the complete list of bookmarks
*/
var listOfBookmarks	= "";


////////////////////////////////////////////////////////////////////////////////
//
// AUXILIARY FUNCTIONS
// ------------------
// Auxiliary function (checking, normalizing...)
//
////////////////////////////////////////////////////////////////////////////////


/*!
** @brief Determines if the application is running on a windows system
** @author Adrien MOGENET
** @return bool : Is a Win32 system ?
** @since 0.1b2
*/
function isWin32System ()
{
    return (navigator.platform == "Win32")
}

/*!
** @brief Checks if the string is a filename or a path
** @param string : path
** @return bool
** @since 0.1rc
** @author Adrien MOGENET
*/
function isAFilename (path)
{
    var length = path.length;

    if (path.substr (length - 4, length) == ".htm")
	return true;
    if (path.substr (length - 5, length) == ".html")
	return true;
    if (path.substr (length - 4, length) == ".xml")
	return true;

    return false;
}

/*!
** @brief Nomalize the path (with a first and final slash) and filename
** @param string: path
** @return string : normalized path, ie /path/to/bookmarks.html
** @since 0.1rc
** @author Adrien MOGENET
*/
function normalizePath (path)
{
    if (path == "")				// default case
	return "/bookmarks.html";
    if (path[0] != '/')				// slash at beginning
	path = "/" + path;
    if (!isAFilename (path))			// final slash if needed
    {
	if (path[path.length - 1] == '/')
	    return path + "bookmarks.html"
	else
	    return path + "/bookmarks.html";
    }
    else
	return path;
}

/*!
** @brief Get the contents from a chrome:// URL
** @param string : chrome URL
** @return string : contents
** @since 0.1rc
** @author Adrien MOGENET
*/
function getChromeContent (aURL)
{
    var ioService	= Components.classes["@mozilla.org/network/io-service;1"]
			.getService (Components.interfaces.nsIIOService);
    var sStream		= Components.classes["@mozilla.org/scriptableinputstream;1"]
			.getService (Components.interfaces.nsIScriptableInputStream);
    var channel		= ioService.newChannel (aURL,null,null);
    var input		= channel.open ();
    var str		= "";

    sStream.init (input);
    str = sStream.read (input.available ());
    sStream.close ();
    input.close();

    return str;
}

/*!
** @brief Get the contents from a absolute local path
** @param string : path
** @return string : contents
** @since 0.1rc
** @todo param = nsIFile ?
** @author Adrien MOGENET
*/
function getLocalContent (filename)
{
    var file	= Components.classes["@mozilla.org/file/local;1"]
		.createInstance (Components.interfaces.nsILocalFile);
    var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
		.createInstance (Components.interfaces.nsIFileInputStream);
    var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
		.createInstance (Components.interfaces.nsIScriptableInputStream)
    var data	= "";;

    file.initWithPath (filename);
    fstream.init (file, -1, 0, 0);
    sstream.init (fstream);

    var str = sstream.read (4096);
    while (str.length > 0)
    {
	data += str;
	str = sstream.read (4096);
    }
    listOfBookmarks += data;
    sstream.close();
    fstream.close();

    return data;
}


////////////////////////////////////////////////////////////////////////////////
//
// FEATURES FUNCTIONS
// ------------------
// Implementation of the different features
//
////////////////////////////////////////////////////////////////////////////////


/*!
** @brief upload the bookmarks on the server.
** The file is uploaded with the favorite method of the user (FTP, HTTP...)
** @see var @c uploader, in uploader.js
** @todo think about the port option.
** @bug no error when invalid password
** @author Adrien MOGENET
*/
function uploadBookmarks ()
{
    var success;
    var scheme	 = prefSchemeGet ();
    var user	 = prefUserGet ();
    var password = prefPasswordGet (user);
    var host	 = prefHostGet ();
    var path	 = prefPathGet ();
    var port	 = 21;
    var content	 = getBookmarks ();
    path	 = normalizePath (path);
    success	 = uploader.upload (scheme, user, password, host, path, port, content);
}

/*!
** @brief create file with bookmarks on hdd
** @author Adrien MOGENET
** @return bool : success or not
** @todo Handle errors (directory doesn't exist...)
*/
function copyBookmarks ()
{
    var filedir	 = prefLocalPathGet ();
    var filename = "bookmarks.html";
    var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                 .createInstance(Components.interfaces.nsIFileOutputStream);
    var file	 = Components.classes["@mozilla.org/file/local;1"]
		 .createInstance(Components.interfaces.nsILocalFile);
    var data	 = getBookmarks ();

    if (DEBUG)
	alert ('Copy bookmarks on HDD : ' + filename);
    if (isWin32System ())
	file.initWithPath (filedir + '\\' + filename);
    else
	file.initWithPath (filedir + '/' + filename);
    foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
    foStream.write (data, data.length);
    foStream.close ();
}

/*!
** @brief Copy styles from the stylesheet to the output file
** @param string : filename
** @since 0.1rc
** @author Adrien MOGENET
*/
function addStyleSheet (filename)
{
    listOfBookmarks += "<style type=\"text/css\">";
    listOfBookmarks += getChromeContent ("chrome://keepmybookmarks/skin/bookstyle.css");
    listOfBookmarks += "</style>";
}

/*!
** @brief Recursive function. Complete the list of bookmarks.
** Fill the @c listOfBookmarks global variable.
** @param long long int : id
** @todo Get the favicon.
** @author Adrien MOGENET
*/
function exploreBookmarksFolder (id)
{
    var options		= historyService.getNewQueryOptions ();
    var query		= historyService.getNewQuery ();
    var folder		= id;

    query.setFolders ([folder], 1);

    var result		= historyService.executeQuery (query, options);
    var rootNode	= result.root;

    rootNode.containerOpen = true;
    for (var i = 0; i < rootNode.childCount; i ++)
    {
	var node = rootNode.getChild (i);

	if (node.type == node.RESULT_TYPE_FOLDER)
	{
	    listOfBookmarks += '<dt><h3> ' + node.title + '</h3><dl><p>';
	    exploreBookmarksFolder (node.itemId);
	    listOfBookmarks += '</p></dl>';
	}
	else
	{
	    listOfBookmarks += '<dt><a href="'+ node.uri +'">' + node.title + '</a>';
	    listOfBookmarks += '<dd>';
	}
    }
    rootNode.containerOpen = false;
}

/*!
** @brief Generates the list of bookmarks.
** @return string : bookmaks
** @author Adrien MOGENET
*/
function getBookmarks ()
{
    var bookmarks = "";
    listOfBookmarks += '<html><head><link rel="stylesheet" type="text/css" href="bookstyle.css"/><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">';
    addStyleSheet ('default');
    listOfBookmarks += '<title>Bookmarks</title></head><body><h1>Bookmarks Menu</h1><dl><p>';
    exploreBookmarksFolder (root);
    listOfBookmarks += '</p></dl></body></html>';
    bookmarks = listOfBookmarks;
    listOfBookmarks = "";
    return bookmarks;
}

/*!
** @brief Main function. Performs all the actions
** Export the bookmarks on a server, export on a specified location.
** @author Adrien MOGENET
*/
function keepmybookmarks ()
{

    if (prefNetworkModeGet ())
	uploadBookmarks ();

    if (prefLocalModeGet ())
	copyBookmarks ();
}

/*!
** @brief Show a folder picker.
** @return string : path of selected folder
** @author Adrien MOGENET
*/
function selectFolder ()
{
    var picker	= Components.classes["@mozilla.org/filepicker;1"]
		.createInstance(nsIFilePicker);
    var path	= "";

    picker.init (window, "Chose a folder", nsIFilePicker.modeGetFolder);
    picker.appendFilters (nsIFilePicker.filterAll | nsIFilePicker.filterText);

    var rv	= picker.show();

    if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace)
	path = picker.file.path;
    return path;
}


////////////////////////////////////////////////////////////////////////////////
//
// EVENT FUNCTIONS
// ---------------
// Following functions are called according to specific events
// ie: app loaded, button pressed...
//
////////////////////////////////////////////////////////////////////////////////


/*!
** @brief When @c choseFolder button is pressed
** @author Adrien MOGENET
*/
function onChoseFolder ()
{
    var path	= selectFolder ();
    document.getElementById ('localpath').value = path;
}

/*!
** @brief When @c OK button of the app is pressed.
** We save all the preferences of the user.
** @see preferences.js, function @c savePreferences()
** @author Adrien MOGENET
*/
function appValidate ()
{
    savePreferences ();
}

/*!
** @brief When 'Cancel' button of the app is pressed
** @author Adrien MOGENET
*/
function appCancel ()
{
    return ;
}

/*!
** @brief When 'Apply' button of the app is pressed.
** Here, XUL application doesn't exit but we save Preferences.
** @see preferences.js, function @c savePreferences()
** @author Adrien MOGENET
*/
function appApply ()
{
    savePreferences ();
    keepmybookmarks ();
}

/*!
** @brief When the app is loaded.
** The different fields are initialized with stored values.
** @see preferences.js, with function @c retrievePreferences()
** @author Adrien MOGENET
*/
function appLoad ()
{
    retrievePreferences ();
    getBookmarks();
}


////////////////////////////////////////////////////////////////////////////////
//
// DEBUG FUNCTIONS
// ---------------
// funcion directly called during development, but only during the
// development
//
////////////////////////////////////////////////////////////////////////////////


function openMainWindow ()
{
    window.open("chrome://keepmybookmarks/content/keepmybookmarks.xul", "bmarks", "chrome,width=430,height=520");
}

